﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
//using System.Windows.Forms.Integration;
using System.Windows;

namespace Flame.Controls
{
    /// <summary>
    /// Convert any .net type to a System.Windows.Forms.Control or a
    /// Table of System.Windows.Forms.Control, ready for being added to
    /// Panel.Children
    /// </summary>
    internal class ControlConverter
    {
        const int IntraLineDistance = 1;

        #region measure Display String functions
        /// <summary>
        /// http://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitations
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="text"></param>
        /// <param name="font"></param>
        /// <returns></returns>
        static System.Drawing.Size _measureDisplayString(Graphics graphics, string text, Font font)
        {
            SizeF size = graphics.MeasureString(text + text, font);
            float singleWidth = graphics.MeasureString(text, font).Width;
            float textWidth = size.Width - singleWidth;
            return new System.Drawing.Size((int)Math.Floor(textWidth) + 3, (int)Math.Floor(size.Height) + 3);
        }

        static System.Drawing.Size measureDisplayString(Graphics graphics, string text, Font font)
        {
            int w = 0;
            int h = 0;
            string[] ss = text.Split('\n', '\r');
            foreach (var s in ss)
            {
                System.Drawing.Size sizeT = _measureDisplayString(graphics, s, font);
                if (w < sizeT.Width) w = sizeT.Width;
                h += sizeT.Height;
            }
            return new System.Drawing.Size(w, h);
        }

        static System.Drawing.Size MeasureDisplayString(Control control, string text, Font font)
        {
            return measureDisplayString(control.CreateGraphics(), text, font);
        }

        static System.Drawing.Size ___measureDisplayString(Graphics graphics, string text, Font font)
        {
            System.Drawing.StringFormat format = new System.Drawing.StringFormat();
            System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, 1000, 1000);
            System.Drawing.CharacterRange[] ranges = new System.Drawing.CharacterRange[] { new System.Drawing.CharacterRange(0, text.Length) };

            format.SetMeasurableCharacterRanges(ranges);

            System.Drawing.Region[] regions = graphics.MeasureCharacterRanges(text, font, rect, format);
            rect = regions[0].GetBounds(graphics);

            return new System.Drawing.Size((int)(rect.Width + 1.0f), (int)(rect.Height + 1.0f));
        }

        #endregion

        static RichTextBox getRichTextBox(string text, Color color)
        {
            RichTextBox rtb = new RichTextBox();
            rtb.ScrollBars = RichTextBoxScrollBars.None;
            rtb.ForeColor = color;
            rtb.BorderStyle = BorderStyle.None;
            rtb.WordWrap = false;
            rtb.ReadOnly = true;
            rtb.Text = text;
            rtb.Size = measureDisplayString(rtb.CreateGraphics(), rtb.Text, rtb.Font);

            rtb.Height = (rtb.Font.Height + IntraLineDistance) * rtb.Lines.Length + 2 + 5 + 5;
            rtb.Location = new System.Drawing.Point(1, 1);
            return rtb;
        }

        static string GetExceptionDetails(Exception exception)
        {
            return "Exception: " + exception.GetType()
                + "\r\nInnerException: " + exception.InnerException
                + "\r\nMessage: " + exception.Message
                + "\r\nStackTrace: " + exception.StackTrace;
        }

        static Control get(string s)
        {
            return getRichTextBox(s, Color.Black);
        }
        static Control get(string s, Color color)
        {
            return getRichTextBox(s, color);
        }
        public static Control Get(Exception e)
        {
            Button b = new Button();
            b.FlatStyle = FlatStyle.Flat;
            b.Text = e.Message;
            b.ForeColor = Color.Red;
            b.AutoSize = true;
            b.Click += (s, _e) => { System.Windows.Forms.MessageBox.Show(GetExceptionDetails(e)); };
            return b;
        }

        static Control get(Flame.Dlr.CodeDomCompiler.CompilationResult e)
        {
            return Get(e.ResultAssembly.Exception);
        }

        /*static Control get(System.Windows.UIElement control)
        {
            ElementHost elementHost = new ElementHost();
            elementHost.Dock = DockStyle.None;
            elementHost.Width = 150;
            elementHost.Height = 50;
            elementHost.Child = control;

            System.Windows.FrameworkElement fe = control as System.Windows.FrameworkElement;
            if (fe == null)
                elementHost.Height = 100;
            else
                elementHost.Height = (int)fe.ActualHeight + 1;
            return elementHost;
        }*/

        static Control getControlSingle(dynamic d)
        {
            if (d.GetType().Namespace != null && d.GetType().Namespace.Contains("IronRuby.Builtins"))
            {
                return get(d.ToString());
            }
            else if (d is Exception)
            {
                return Get(d as Exception);
            }
            else if (d is Control)
            {
                return d;
            }
            /*else if (d is UIElement)
            {
                return get(d as UIElement);
            }*/
            else if (d is Flame.Dlr.CodeDomCompiler.CompilationResult)
            {
                return get(d as Flame.Dlr.CodeDomCompiler.CompilationResult);
            }
            else
            {
                try
                {
                    return get((d as Object).ToString());
                }
                catch 
                { 
                    return get(d.ToString()); 
                }
                
            }
        }

        static void fromOneControl(List<List<Control>> controls, Control c)
        {
            List<Control> cs = new List<Control>();
            cs.Add(c);
            controls.Add(cs);
        }

        static Type[] checkIfEnumerable(Type type)
        {
            List<Type> genericsTypes = new List<Type>();
            foreach (Type intType in type.GetInterfaces())
            {
                if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
                {
                    genericsTypes.AddRange(intType.GetGenericArguments());
                }
            }
            return genericsTypes.ToArray();
        }

        static bool checkIfNull(dynamic d)
        {
            try
            {
                if (d == null) return true;
            }
            catch { }
            return false;
        }

        static bool checkForFalseEnumerableType(dynamic d, List<List<Control>> controls)
        {
            try
            {
               //if (d is System.Management.Automation.PSObject)
                if (d.GetType().FullName == "System.Management.Automation.PSObject")
                {
                    fromOneControl(controls, getControlSingle(d));
                    return true;
                }
                else if (d is string)
                {
                    fromOneControl(controls, getControlSingle(d));
                    // Handle STRING ( !: string is Enumerable) 
                    return true;
                }

            }
            catch { }

            return false;
        }

        public static List<List<Control>> GetControl(dynamic d, bool firstpass = true)
        {
            List<List<Control>> controls = new List<List<Control>>();

            if (checkForFalseEnumerableType(d, controls)) return controls;
            if (checkIfNull(d)) return controls;

            Type[] t1 = checkIfEnumerable(d.GetType());
            if (t1.Length == 0)
            {
                fromOneControl(controls, getControlSingle(d));
            }
            else
            {
                if (firstpass)
                {
                    foreach (dynamic d2 in d)
                    {
                        List<List<Control>> controls2 = GetControl(d2, false);
                        if (controls2.Count > 0)
                            controls.Add(controls2[0]);
                    }
                }
                else
                {
                    List<Control> cs = new List<Control>();
                    foreach (dynamic d2 in d)
                        cs.Add(getControlSingle(d2));
                    controls.Add(cs);
                }
            }
            return controls;
        }
    }
}
           